package com.op.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.op.constants.NumberConstant;
import com.op.entity.Role;
import com.op.entity.User;
import com.op.mapper.UserMapper;
import com.op.model.vo.UserInfoVO;
import com.op.service.UserService;
import com.op.util.FileUtil;
import io.minio.errors.MinioException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * (User)表服务实现类
 *
 * @author 孤焰
 * @since 2021-09-28 19:53:56
 */
@Service("userService")
public class UserServiceImpl extends MPJBaseServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private BCryptPasswordEncoder bCryptPassword;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public final String REDIS_PREFIX_TEMP_ACCOUNT = "system:user:tempAccount";

    @Override
    public User login(String username, String password) throws Exception {
        // 先查看redis中是否存在临时用户
        ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
        String redisUserInfo = stringStringValueOperations.get(REDIS_PREFIX_TEMP_ACCOUNT + ":" + username);
        if (!StringUtils.isEmpty(redisUserInfo)) {
            User userDb = JSONObject.parseObject(redisUserInfo, User.class);
            if (!userDb.getPassword().equals(password)) {
                throw new Exception("用户名或密码错误");
            }
            return userDb;
        } else {
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(User::getStatus, NumberConstant.ENABLE)
                    .eq(User::getUsername, username)
                    .or()
                    .eq(User::getPhone, username)
                    .or()
                    .eq(User::getEmail, username);
            User userDb = userMapper.selectOne(lambdaQueryWrapper);
            // 判断是否存在 判断当前用户判断密码是否正确
            if (userDb == null || !bCryptPassword.matches(password, userDb.getPassword())) {
                throw new Exception("用户名或密码错误");
            }

            return userDb;
        }
    }

    @Override
    public boolean changePassword(User user) {
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
        lambdaUpdateWrapper.eq(User::getId, user.getId())
                .set(User::getPassword, bCryptPassword.encode(user.getPassword()));
        return userMapper.update(user, lambdaUpdateWrapper) > 0;
    }

    @Override
    public Page<UserInfoVO> page(Page<UserInfoVO> page, User user) {
        MPJLambdaWrapper<User> lambdaWrapper = new MPJLambdaWrapper<>();
        lambdaWrapper
                .selectAll(User.class)
                .select(Role::getRoleName)
                .innerJoin(Role.class, Role::getId, User::getRoleId)
                .eq(user.getStatus() != null, User::getStatus, user.getStatus())
                .like(user.getUsername() != null, User::getUsername, user.getUsername())
                .orderByDesc(User::getStatus, User::getModifyTime, User::getId);
        return (Page<UserInfoVO>) userMapper.selectJoinPage(page, UserInfoVO.class, lambdaWrapper);
    }

    @Override
    public Integer notReview() {
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(User::getStatus, NumberConstant.NOT_REVIEW);
        return userMapper.selectCount(lambdaQueryWrapper);
    }

    @Override
    public boolean check(Long id, String value) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.and(wrapper ->
                wrapper.eq(User::getUsername, value)
                        .or().eq(User::getPhone, value)
                        .or().eq(User::getEmail, value)
                );
        if (id != null) {
            lambdaQueryWrapper.ne(User::getId, id);
        }
        return userMapper.selectCount(lambdaQueryWrapper) == 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveOrUpdate(User user, MultipartFile avatarPath) throws MinioException, NoSuchAlgorithmException, InvalidKeyException, IOException {
        if (avatarPath != null) {
            user.setAvatarPath(FileUtil.uploadImg(avatarPath));
        }
        if (user.getPassword() != null) {
            user.setPassword(bCryptPassword.encode(user.getPassword()));
        }
        // id == null 则为添加逻辑，否则为编辑逻辑
        if (user.getId() == null) {
            return userMapper.insert(user) > 0;
        } else {
            // 此编辑逻辑只适用于管理员和已登录的用户，不需要核验旧密码，直接覆盖新密码
            LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
            lambdaUpdateWrapper.eq(User::getId, user.getId())
                    .set(User::getUsername, user.getUsername())
                    .set(user.getPassword() != null, User::getPassword, user.getPassword())
                    .set(User::getPhone, user.getPhone())
                    .set(User::getEmail, user.getEmail())
                    .set(User::getRoleId, user.getRoleId())
                    .set(user.getAvatarPath() != null, User::getAvatarPath, user.getAvatarPath());
            return userMapper.update(user, lambdaUpdateWrapper) > 0;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean logicDelBatchById(List<Long> idList) {
        for (Long id : idList) {
            User user = new User();
            user.setId(id);
            user.setStatus(NumberConstant.DISABLE);
            if (userMapper.updateById(user) <= 0) {
                throw new RuntimeException("部分删除失败");
            }
        }
        return true;
    }

    @Override
    public boolean registerTempAccount(User user) {
        // 生成随机密码和分配角色
        String password = UUID.randomUUID().toString().substring(0, 11).replaceAll("-", "");
        // 由于存在redis中，并且该账号不具有私人数据，所以账号密码不做加密处理
        user.setPassword(password);
        Long tempRoleId = 9L;
        user.setRoleId(tempRoleId);
        user.setStatus(1);
        ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
        stringStringValueOperations.set(REDIS_PREFIX_TEMP_ACCOUNT + ":" + user.getPhone(),
                JSONObject.toJSONString(user),
                3,
                TimeUnit.HOURS);
        return true;
    }

    @Override
    public boolean existUserByRoleId(Long roleId) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(User::getRoleId, roleId);
        Integer count = userMapper.selectCount(lambdaQueryWrapper);
        return count > 0;
    }
}

